package com.timeriver.service.impl;

import com.timeriver.domain.dto.SparkModelDto;
import com.timeriver.domain.entity.Model;
import com.timeriver.domain.entity.ModelVersion;
import com.timeriver.domain.vo.ModelsVO;
import com.timeriver.domain.vo.ModelVO;
import com.timeriver.domain.vo.UploadFileVO;
import com.timeriver.domain.vo.VersionVO;
import com.timeriver.repository.ModelRepository;
import com.timeriver.repository.ModelVersionRepository;
import com.timeriver.service.ModelService;
import com.timeriver.utils.EntityConvertUtil;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.*;
import java.util.Date;
import java.util.List;

@Service
public class ModelServiceImpl implements ModelService {

    @Value("${predict.uri}")
    private String predictUrl;

    @Autowired
    private ModelRepository repository;

    @Autowired
    private ModelVersionRepository versionRepository;

    @Override
    public void saveModelInfo(SparkModelDto modelDto) throws Exception {
        if (checkNameExistOrNot(modelDto.getName())) {
            throw new Exception("模型名字重复！请重新修改");
        }
        Model model = EntityConvertUtil.object2DtoOrEntity(modelDto, Model.class);
        repository.save(model);
    }

    /**
     * 数据库同名校验
     * @param name
     * @return
     */
    private Boolean checkNameExistOrNot(String name) {
        Model byName = repository.findByNameAndDisabledEquals(name, 0);
        return byName == null ? false : true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void uploadFile(UploadFileVO uploadFile) throws Exception {
        Model model = repository.getOne(uploadFile.getId());

        File modelFile = new File(model.getModelPath(), uploadFile.getFile().getOriginalFilename());
        /** 上传文件 */
        uploadFile.getFile().transferTo(modelFile);
        /** 解压文件 */
        try (ZipArchiveInputStream inputStream = this.getZipFile(modelFile)) {
            ZipArchiveEntry entry = null;
            while ((entry = inputStream.getNextZipEntry()) != null) {
                System.out.println("----->unzip：" + entry.getName());
                if (entry.isDirectory()) {
                    File directory = new File(model.getModelPath(), entry.getName());
                    directory.mkdirs();
                } else {
                    OutputStream os = null;
                    try {
                        os = new BufferedOutputStream(new FileOutputStream(new File(model.getModelPath(), entry.getName())));
                        System.out.println("解压文件的当前路径为:{}" + model.getModelPath() + entry.getName());
                        IOUtils.copy(inputStream, os);
                    } finally {
                        IOUtils.closeQuietly(os);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        /** 删除压缩包 */
        modelFile.delete();
        String path = StringUtils.substringBefore(modelFile.getAbsolutePath(), ".zip");
        if (new File(path).exists()) {
            model.setModelPath(path);
            repository.save(model);
        }
    }

    @Override
    public ModelsVO getModelList(PageRequest pageRequest) throws Exception {
        Page all = repository.findAll(getExistModels(), pageRequest);
        List content = all.getContent();
        List<ModelVO> vos = EntityConvertUtil.objects2DtosOrEntitys(content, ModelVO.class);
        String uri = "http://" + predictUrl + "/rest/predict";
        return new ModelsVO(vos, all.getTotalElements(), pageRequest.getPageNumber(), uri);
    }

    public ModelsVO getModelList(PageRequest pageRequest, String search) throws Exception {
        Page all = repository.findAll(getSearchModels(search), pageRequest);
        List content = all.getContent();
        List<ModelVO> vos = EntityConvertUtil.objects2DtosOrEntitys(content, ModelVO.class);
        String uri = "http://" + predictUrl + "/rest/predict";
        return new ModelsVO(vos, all.getTotalElements(), pageRequest.getPageNumber(), uri);
    }

    @Override
    public void deleteModel(String id) throws Exception {
        Model model = repository.getOne(id);
        model.setDisabled(1);
        model.setUpdateTime(new Date());
        repository.save(model);
    }

    /**
     * 获取版本号文件列表
     * @param id
     * @return
     */
    @Override
    public List<VersionVO> getModelVersion(String id) throws Exception {
        List<ModelVersion> versions = versionRepository.getAllByModelId(id);
        return EntityConvertUtil.objects2DtosOrEntitys(versions, VersionVO.class);
    }

    @Override
    public Integer setModelVersion(String id, Integer version) throws Exception {
        Model one = repository.getOne(id);
        one.setCurrentVersion(version);
        repository.save(one);
        return one.getCurrentVersion();
    }

    /**
     * 获取压缩文件数据流
     * @param zipFile
     * @return
     * @throws Exception
     */
    private ZipArchiveInputStream getZipFile(File zipFile) throws Exception {
        return new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
    }

    /**
     * 获取未被删除的模型文件
     * @return
     */
    private Specification<Model> getExistModels() {
        return new Specification<Model>() {
            @Override
            public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get("disabled"), 0);
            }
        };
    }

    /**
     * 获取关键字模糊匹配的模型文件
     *      1. name属性
     *      2. description属性
     * @return
     */
    private Specification<Model> getSearchModels(String search) {
        return new Specification<Model>() {
            @Override
            public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Predicate description = criteriaBuilder.like(root.get("description"), "%" + search + "%");
                Predicate name = criteriaBuilder.like(root.get("name"), "%" + search + "%");
                return criteriaBuilder.or(description, name);
            }
        };
    }
}
